use sea_orm::{EntityTrait, QueryFilter, Select, prelude::Expr};

pub use tin_sea_geo_macros::{with_centroid, with_geo, with_geometry};

/// 空间查询扩展 trait
///
/// 为 SeaORM 的 Select 查询添加地理空间查询功能
pub trait SpatialQueryExt<E>
where
    E: EntityTrait,
{
    /// 点包含查询 - 查找包含指定点的几何对象
    ///
    /// 使用默认的 `geom` 字段和 WGS84(4326) 到 CGCS2000(4527) 坐标转换
    ///
    /// # 参数
    /// * `lng` - 经度
    /// * `lat` - 纬度
    ///
    /// # 示例
    /// ```
    /// use my_macros::SpatialQueryExt;
    ///
    /// let result = Entity::find()
    ///     .contains(116.26953, 39.74616)
    ///     .all(&db)
    ///     .await?;
    /// ```
    fn contains(self, lng: f64, lat: f64) -> Select<E>;

    /// 自定义点包含查询 - 查找包含指定点的几何对象
    ///
    /// 可以指定数据库字段名和坐标系转换参数
    ///
    /// # 参数
    /// * `lng` - 经度
    /// * `lat` - 纬度
    /// * `field` - 数据库中的几何字段名
    /// * `from_srid` - 源坐标系 SRID
    /// * `to_srid` - 目标坐标系 SRID
    ///
    /// # 示例
    /// ```
    /// use my_macros::SpatialQueryExt;
    ///
    /// let result = Entity::find()
    ///     .contains_custom(116.26953, 39.74616, "geometry".to_string(), 4326, 4527)
    ///     .all(&db)
    ///     .await?;
    /// ```
    fn contains_custom(
        self,
        lng: f64,
        lat: f64,
        field: String,
        from_srid: u32,
        to_srid: u32,
    ) -> Select<E>;

    /// 多边形包含查询 - 查找完全在指定多边形内的几何对象
    ///
    /// 使用默认的 `geom` 字段和 WGS84(4326) 到 CGCS2000(4527) 坐标转换
    /// 多边形会自动闭合（如果首末点不同）
    ///
    /// # 参数
    /// * `points` - 多边形顶点坐标数组，支持 Vec、数组、切片等类型
    ///
    /// # 示例
    /// ```
    /// use my_macros::SpatialQueryExt;
    ///
    /// let polygon = vec![
    ///     (116.0, 39.0),
    ///     (117.0, 39.0),
    ///     (117.0, 40.0),
    ///     (116.0, 40.0),
    /// ];
    ///
    /// let result = Entity::find()
    ///     .within(polygon)
    ///     .all(&db)
    ///     .await?;
    /// ```
    fn within<P>(self, points: P) -> Select<E>
    where
        P: AsRef<[(f64, f64)]>;

    /// 自定义多边形包含查询 - 查找完全在指定多边形内的几何对象
    ///
    /// 可以指定数据库字段名和坐标系转换参数
    /// 多边形会自动闭合（如果首末点不同）
    ///
    /// # 参数
    /// * `points` - 多边形顶点坐标数组，支持 Vec、数组、切片等类型
    /// * `field` - 数据库中的几何字段名
    /// * `from_srid` - 源坐标系 SRID
    /// * `to_srid` - 目标坐标系 SRID
    ///
    /// # 示例
    /// ```
    /// use my_macros::SpatialQueryExt;
    ///
    /// let polygon = [(116.0, 39.0), (117.0, 39.0), (117.0, 40.0), (116.0, 40.0)];
    ///
    /// let result = Entity::find()
    ///     .within_custom(polygon, "geometry".to_string(), 4326, 4527)
    ///     .all(&db)
    ///     .await?;
    /// ```
    fn within_custom<P>(self, points: P, field: String, from_srid: u32, to_srid: u32) -> Select<E>
    where
        P: AsRef<[(f64, f64)]>;
}

impl<E> SpatialQueryExt<E> for Select<E>
where
    E: EntityTrait,
{
    fn contains(self, lng: f64, lat: f64) -> Select<E> {
        let expr = Expr::cust(format!(
            "ST_Contains( geom, ST_Transform( ST_SetSRID(ST_MakePoint({lng}, {lat}), 4326), 4527 ))"
        ));
        self.filter(expr)
    }

    fn contains_custom(
        self,
        lng: f64,
        lat: f64,
        field: String,
        from_srid: u32,
        to_srid: u32,
    ) -> Select<E> {
        let expr = Expr::cust(format!(
            "ST_Contains( {field}, ST_Transform( ST_SetSRID(ST_MakePoint({lng}, {lat}), {from_srid}), {to_srid} ))"
        ));
        self.filter(expr)
    }

    fn within<P>(self, points: P) -> Select<E>
    where
        P: AsRef<[(f64, f64)]>,
    {
        let wkt = build_polygon_wkt(points.as_ref());
        let expr = Expr::cust(format!(
            "ST_Within( geom, ST_Transform( ST_SetSRID(ST_GeomFromText('{wkt}'), 4326), 4527 ))"
        ));
        self.filter(expr)
    }

    fn within_custom<P>(self, points: P, field: String, from_srid: u32, to_srid: u32) -> Select<E>
    where
        P: AsRef<[(f64, f64)]>,
    {
        let wkt = build_polygon_wkt(points.as_ref());
        let expr = Expr::cust(format!(
            "ST_Within( {field}, ST_Transform( ST_SetSRID(ST_GeomFromText('{wkt}'), {from_srid}), {to_srid} ))"
        ));
        self.filter(expr)
    }
}

/// 构建多边形的 WKT (Well-Known Text) 格式字符串
///
/// 将坐标点数组转换为 PostGIS 可识别的多边形 WKT 格式
/// 自动处理多边形闭合逻辑
///
/// # 参数
/// * `points` - 多边形顶点坐标数组
///
/// # 返回值
/// * 多边形的 WKT 格式字符串，如 "POLYGON((116 39, 117 39, 117 40, 116 40, 116 39))"
/// * 如果输入为空则返回 "POLYGON EMPTY"
fn build_polygon_wkt(points: &[(f64, f64)]) -> String {
    if points.is_empty() {
        return "POLYGON EMPTY".to_string();
    }

    let mut coordinates = Vec::new();
    for (lng, lat) in points {
        coordinates.push(format!("{lng} {lat}"));
    }

    if let (Some(first), Some(last)) = (points.first(), points.last()) {
        if first != last {
            let (first_lng, first_lat) = first;
            coordinates.push(format!("{first_lng} {first_lat}"));
        }
    }

    format!("POLYGON(({}))", coordinates.join(", "))
}
